package com.qisimanxiang.dalaran.zuul.custom.concurrent;

import com.qisimanxiang.dalaran.zuul.config.SentinelConfig;
import com.qisimanxiang.dalaran.zuul.redis.RedisHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author wangmeng
 * @date created in 10:10 上午 2019/9/18
 * @modified by
 */
@Slf4j
public class ThreadSemaphore {
    private static Map<String, Semaphore> semaphoreMap = new HashMap<>();
    private static Map<String, Integer> configMap = new HashMap<>();

    public static void updateSemaphoreMap(Map<String, Integer> map) {
        if (map == null) {
            semaphoreMap = new HashMap<>();
        } else {
            configMap = map;
            Integer instanceNum = SentinelConfig.getTotalInstanceNum();
            semaphoreMap = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> new Semaphore(entry.getValue() / instanceNum)));
        }
        log.info("ThreadSemaphore.semaphoreMap update success.");
    }


    public static boolean tryAcquire(String serviceId, String api, String customer) {
        if (StringUtils.isEmpty(serviceId)) {
            return true;
        }
        if (StringUtils.isEmpty(api)) {
            return tryAcquire(serviceId);
        }
        if (StringUtils.isEmpty(customer)) {
            return tryAcquire(serviceId + "@" + api);
        }
        return tryAcquire(serviceId + "@" + api + "@" + customer);
    }

    private static boolean tryAcquire(String key) {
        if (RedisHelper.available) {
            return tryRedisAcquire(key);
        }
        return tryLoclAcquire(key);
    }

    private static boolean tryLoclAcquire(String key) {
        Semaphore semaphore = semaphoreMap.get(key);
        if (semaphore == null) {
            return true;
        }
        try {
            return semaphore.tryAcquire(200, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            return false;
        }
    }

    private static boolean tryRedisAcquire(String key) {
        Integer total = configMap.get(key);
        if (total == null) {
            return true;
        }
        return RedisHelper.tryAcquire(key, total, 200);

    }

    public static void release(String serviceId, String api, String customer) {
        if (StringUtils.isEmpty(serviceId)) {
            return;
        }
        if (StringUtils.isEmpty(api)) {
            release(serviceId);
            return;
        }
        if (StringUtils.isEmpty(customer)) {
            release(serviceId + "@" + api);
            return;
        }
        release(serviceId + "@" + api + "@" + customer);
    }


    private static void release(String key) {
        if (RedisHelper.available) {
            releaseRedis(key);
            return;
        }
        releaseLocal(key);
    }

    private static void releaseLocal(String key) {
        Semaphore semaphore = semaphoreMap.get(key);
        if (semaphore != null) {
            semaphore.release();
        }
    }

    private static void releaseRedis(String key) {
        RedisHelper.release(key);
    }
}
